package com.zeyu.framework.tools.report.dynamic;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zeyu.framework.core.persistence.table.Field;
import com.zeyu.framework.tools.report.charts.ExtendChartData;
import com.zeyu.framework.utils.StringUtils;
import net.sf.dynamicreports.jasper.builder.JasperReportBuilder;
import net.sf.dynamicreports.jasper.builder.export.JasperPdfExporterBuilder;
import net.sf.dynamicreports.jasper.builder.export.JasperXlsExporterBuilder;
import net.sf.dynamicreports.jasper.constant.JasperProperty;
import net.sf.dynamicreports.report.builder.column.ColumnBuilder;
import net.sf.dynamicreports.report.builder.column.TextColumnBuilder;
import net.sf.dynamicreports.report.builder.component.ComponentBuilder;
import net.sf.dynamicreports.report.builder.expression.Expressions;
import net.sf.dynamicreports.report.builder.style.StyleBuilder;
import net.sf.dynamicreports.report.constant.HorizontalTextAlignment;
import net.sf.dynamicreports.report.constant.ImageScale;
import net.sf.dynamicreports.report.constant.PageType;
import net.sf.dynamicreports.report.constant.VerticalTextAlignment;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRRewindableDataSource;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.ImageIcon;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static net.sf.dynamicreports.report.builder.DynamicReports.cmp;
import static net.sf.dynamicreports.report.builder.DynamicReports.col;
import static net.sf.dynamicreports.report.builder.DynamicReports.export;
import static net.sf.dynamicreports.report.builder.DynamicReports.report;
import static net.sf.dynamicreports.report.builder.DynamicReports.stl;

/**
 * 生成报表.
 * Created by zeyuphoenix on 16/9/4.
 */
public class ReportGenerator {

    // ================================================================
    // Constants
    // ================================================================

    /**
     * logger
     */
    private static final Logger logger = LoggerFactory.getLogger(ReportGenerator.class);

    // ================================================================
    // Fields
    // ================================================================

    // ================================================================
    // Constructors
    // ================================================================

    /**
     * 构造函数
     */
    private ReportGenerator() {
    }

    // ================================================================
    // Methods from/for super Interfaces or SuperClass
    // ================================================================

    // ================================================================
    // Public or Protected Methods
    // ================================================================

    /**
     * 创建JasperReportBuilder,可以用于客户端展示、或者直接生产报表
     *
     * @param title
     *            报表的title、居右
     * @param componentBuilder
     *            报表展示内容
     */
    public static JasperReportBuilder generate(String title,
                                               ComponentBuilder<?, ?>... componentBuilder) {
        JasperReportBuilder report = report();
        report.setTemplate(Templates.reportTemplate)
                .title(Templates.createTitleComponent(title))
                .detail(componentBuilder)
                .setDataSource(createDataSource())
                .pageFooter(Templates.footerComponent);

        return report;
    }

    /**
     * 以客户端形式展示报表,可以分页、保存的操作
     */
    /*public static void buildSwing(String title,
                                  ComponentBuilder<?, ?>... componentBuilder) {

        try {
            JasperReportBuilder report = generate(title, componentBuilder);

            JasperViewer.viewReport(report.toJasperPrint());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/

    /**
     * 将报表保存为PDF格式
     */
    public static void buildPDF(String title, String filePath,
                                ComponentBuilder<?, ?>... componentBuilder) {
        buildPDF(title, filePath, false, null, componentBuilder);
    }

    /**
     * 将报表保存为PDF格式
     */
    public static void buildPDF(String title, String filePath,
                                boolean encrypted, String password,
                                ComponentBuilder<?, ?>... componentBuilder) {

        try {
            // 生产报表
            JasperReportBuilder report = generate(title, componentBuilder);

            // pdf 导出
            JasperPdfExporterBuilder pdfExporter = export.pdfExporter(filePath);
            // 可以加密设置密码
            if (encrypted) {
                pdfExporter.setEncrypted(true).setUserPassword(password);
            }
            report.toPdf(pdfExporter);
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    /**
     * 将报表保存为Excel格式
     */
    public static void buildXLS(String title, String filePath,
                                ComponentBuilder<?, ?>... componentBuilder) {

        try {
            // 生产报表
            JasperReportBuilder report = generate(title, componentBuilder);
            report.addProperty(JasperProperty.EXPORT_XLS_FREEZE_ROW, "2")
                    .ignorePageWidth().ignorePagination();

            // excel 导出
            JasperXlsExporterBuilder xlsExporter = export.xlsExporter(filePath)
                    .setDetectCellType(true).setIgnorePageMargins(true)
                    .setWhitePageBackground(false)
                    .setRemoveEmptySpaceBetweenColumns(true);

            report.toXls(xlsExporter);
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    /**
     * 包含title的表格组件
     */
    public static ComponentBuilder<?, ?> createTableComponent(String title,
                                                              FieldDataSource dataSource) {

        StyleBuilder boldCenteredStyle = stl
                .style(Templates.bold14CenteredStyle)
                .setHorizontalTextAlignment(HorizontalTextAlignment.CENTER)
                .setVerticalTextAlignment(VerticalTextAlignment.MIDDLE);
        StyleBuilder columnTitleStyle = stl
                .style(boldCenteredStyle)
                .setBorder(
                        stl.penThin().setLineWidth(1.1f)
                                .setLineColor(ReportUtils.convert("#dddddd")))
                .setBackgroundColor(ReportUtils.convert("#ededed"));
        StyleBuilder columnStyle = stl.style(Templates.rootStyle).setBorder(
                stl.penThin().setLineWidth(0.8f)
                        .setLineColor(ReportUtils.convert("#dfdddd")));
        ColumnBuilder<?, ?>[] itemColumns = null;
        if (dataSource != null) {
            Field[] columns = dataSource.columns;
            if (ArrayUtils.isNotEmpty(columns)) {
                itemColumns = new TextColumnBuilder<?>[columns.length + 1];
                for (int i = 0; i < columns.length; i++) {
                    // title, field name data type
                    itemColumns[i + 1] = col.column(
                            columns[i].getDisplayName(),
                            columns[i].getFieldName(),
                            columns[i].getFiledClass());
                    if (columns[i].getFieldType() == Field.FieldType.ANGLE) {
                        itemColumns[i + 1] = ((TextColumnBuilder<?>) itemColumns[i + 1])
                                .setHorizontalTextAlignment(HorizontalTextAlignment.LEFT);
                    } else if (columns[i].getFieldType() == Field.FieldType.QUOTA) {
                        itemColumns[i + 1] = ((TextColumnBuilder<?>) itemColumns[i + 1])
                                .setHorizontalTextAlignment(HorizontalTextAlignment.RIGHT);
                    } else {
                        itemColumns[i + 1] = ((TextColumnBuilder<?>) itemColumns[i + 1])
                                .setHorizontalTextAlignment(HorizontalTextAlignment.CENTER);
                    }
                }
            }
        }

        if (itemColumns != null) {
            TextColumnBuilder<?> rowNumberColumn = col
                    .reportRowNumberColumn("No.").setFixedColumns(5)
                    .setHorizontalTextAlignment(HorizontalTextAlignment.CENTER);
            itemColumns[0] = rowNumberColumn;
        }

        JasperReportBuilder report = report();

        report.setColumnTitleStyle(columnTitleStyle)
                .highlightDetailEvenRows()
                .addDetailRowHighlighter(
                        stl.conditionalStyle(Expressions.printInEvenRow())
                                .setBackgroundColor(
                                        ReportUtils.convert("#f5f5f5")))
                .columns(
                        // add columns
                        itemColumns).setDataSource(dataSource)
                .setColumnStyle(columnStyle);
        if (StringUtils.isNotEmpty(title)) {
            report.title(cmp.text(title).setStyle(Templates.bold18CenteredStyle));
        }

        return cmp.subreport(report);
    }

    /**
     * 创建没有title的表格组件
     */
    public static ComponentBuilder<?, ?> createTableComponent(
            FieldDataSource dataSource) {
        return createTableComponent(null, dataSource);
    }

    /**
     * 创建模板形式的title
     */
    public static ComponentBuilder<?, ?> createTitleComponent(String title) {
        return cmp.text(title).setStyle(Templates.bold18CenteredStyle);
    }

    /**
     * 创建一个图片组件,可以插入到报表中的.
     */
    public static ComponentBuilder<?, ?> createImageComponent(ImageIcon image) {
        int height = calculateHeight(image.getIconWidth(),
                image.getIconHeight());
        return cmp.image(image.getImage()).setHeight(height)
                .setImageScale(ImageScale.RETAIN_SHAPE);
    }

    /**
     * 创建一个横向多个图片组件,可以插入到报表中的.
     */
    public static ComponentBuilder<?, ?> createImageComponent(
            ImageIcon... images) {
        if (ArrayUtils.isNotEmpty(images)) {
            int size = images.length;
            ComponentBuilder<?, ?>[] components = new ComponentBuilder<?, ?>[size];
            ImageIcon image = getMaxImage(images);
            int height = calculateHeight(image.getIconWidth(),
                    image.getIconHeight())
                    / size;
            for (int i = 0; i < size; i++) {
                components[i] = cmp.image(images[i].getImage())
                        .setHeight(height)
                        .setImageScale(ImageScale.RETAIN_SHAPE);
            }
            return cmp.horizontalList(components);
        }
        return null;
    }

    /**
     * 创建一个横向图片、表格组件,可以插入到报表中的.
     */
    public static ComponentBuilder<?, ?> createImageTableComponent(
            ImageIcon image, FieldDataSource dataSource) {
        if (image != null && dataSource != null) {
            ComponentBuilder<?, ?>[] components = new ComponentBuilder<?, ?>[2];
            int height = calculateHeight(image.getIconWidth(),
                    image.getIconHeight()) / 3;
            components[0] = cmp.image(image.getImage()).setHeight(height)
                    .setImageScale(ImageScale.RETAIN_SHAPE);
            components[1] = createTableComponent(dataSource);
            return cmp.horizontalList(components);
        }
        return null;
    }

    /**
     * 创建一个包含title的图片组件.
     */
    public static ComponentBuilder<?, ?> createImageComponent(String title,
                                                              ImageIcon image) {
        // title的风格
        StyleBuilder boldCenteredStyle = stl.style(
                Templates.bold18CenteredStyle).setHorizontalTextAlignment(
                HorizontalTextAlignment.CENTER);
        // 创建子report
        JasperReportBuilder report = report();
        report.title(cmp.text(title).setStyle(boldCenteredStyle)).summary(
                createImageComponent(image));
        return cmp.subreport(report);
    }

    /**
     * 创建多个包含title的图片组件.
     */
    public static ComponentBuilder<?, ?> createImageComponent(String[] titles,
                                                              ImageIcon[] images) {
        if (ArrayUtils.isNotEmpty(images)) {
            // title的风格
            StyleBuilder boldCenteredStyle = stl.style(
                    Templates.bold18CenteredStyle).setHorizontalTextAlignment(HorizontalTextAlignment.CENTER);

            int size = images.length;
            ComponentBuilder<?, ?>[] components = new ComponentBuilder<?, ?>[size];
            ImageIcon image = getMaxImage(images);
            int height = calculateHeight(image.getIconWidth(),
                    image.getIconHeight())
                    / size;
            for (int i = 0; i < size; i++) {
                // 创建子report
                JasperReportBuilder report = report();
                if (titles != null && titles.length > i && titles[i] != null) {
                    report.title(
                            cmp.text(titles[i]).setStyle(boldCenteredStyle))
                            .summary(
                                    cmp.image(image.getImage())
                                            .setHeight(height)
                                            .setImageScale(
                                                    ImageScale.RETAIN_SHAPE));
                } else {
                    report.title(cmp.text("").setStyle(boldCenteredStyle))
                            .summary(
                                    cmp.image(image.getImage())
                                            .setHeight(height)
                                            .setImageScale(
                                                    ImageScale.RETAIN_SHAPE));
                }
                components[i] = cmp.subreport(report);
            }
            return cmp.horizontalList(components);
        }

        return null;
    }

    /**
     * 以A4格式计算图片的等比率高度
     */
    private static int calculateHeight(Number width, Number height) {
        float v = width.floatValue();
        float rate = v / PageType.A4.getWidth();
        return Float.valueOf(height.floatValue() / rate).intValue() + 10;
    }

    /**
     * 获得多个图片中最高的image
     */
    private static ImageIcon getMaxImage(ImageIcon... images) {
        ImageIcon image = null;
        if (ArrayUtils.isNotEmpty(images)) {
            image = images[0];
            for (ImageIcon item : images) {
                if (image.getIconHeight() > item.getIconHeight()) {
                    image = item;
                }
            }
        }

        return image;
    }

    /**
     * 根据图的数据创建数据源
     * @param extendChartData 表格数据
     * @return 数据源
     */
    public static FieldDataSource createDataSource(ExtendChartData extendChartData) {
        if (extendChartData != null) {
            return createDataSource(ReportUtils.generateColumnsFromChartData(extendChartData),
                    ReportUtils.generateDatasFromChartData(extendChartData));
        }
        return null;
    }

    /**
     * 根据列名和数据创建数据源
     * @param columns 列名
     * @param datas 数据
     */
    public static FieldDataSource createDataSource(Field[] columns, Object[][] datas) {
        FieldDataSource dataSource = null;

        if (ArrayUtils.isNotEmpty(columns)) {
            dataSource = new FieldDataSource(columns);
            if (ArrayUtils.isNotEmpty(datas)) {
                for (Object[] data : datas) {
                    for (int i = 0; i < data.length; i++) {
                        if (columns[i].getRender() != null) {
                            data[i] = ReportUtils.transform(columns[i].getRender(), data[i]);
                            columns[i].setFiledClass(String.class);
                        }
                    }
                    dataSource.add(data);
                }
            } else {
                Object[] data = new Object[columns.length];
                for (int i = 0; i < data.length; i++) {
                    Class<?> cl = columns[i].getFiledClass();
                    if (cl.isAssignableFrom(Number.class)) {
                        data[i] = 0;
                    } else {
                        data[i] = "";
                    }
                }
                dataSource.add(data);
            }
        }

        return dataSource;
    }

    // ================================================================
    // Getter & Setter
    // ================================================================

    // ================================================================
    // Private Methods
    // ================================================================

    /**
     * 创建一个无数据数据源
     */
    private static JRDataSource createDataSource() {
        return new JREmptyDataSource(1);
    }

    // ================================================================
    // Inner or Anonymous Class
    // ================================================================

    /**
     * 自定义数据源
     */
    protected static class FieldDataSource implements JRRewindableDataSource, Serializable {

        // ================================================================
        // Constants
        // ================================================================

        /**
         * UID
         */
        private static final long serialVersionUID = 1L;

        // ================================================================
        // Fields
        // ================================================================

        // 参数
        private Field[] columns;
        private List<Map<String, Object>> values;
        private Iterator<Map<String, Object>> iterator;
        private Map<String, Object> currentRecord;

        // ================================================================
        // Constructors
        // ================================================================

        /**
         * 构造函数
         */
        public FieldDataSource(Field... columns) {
            this.columns = columns;
            this.values = Lists.newArrayList();
        }

        // ================================================================
        // Methods from/for super Interfaces or SuperClass
        // ================================================================

        @Override
        public Object getFieldValue(JRField field) throws JRException {
            return currentRecord.get(field.getName());
        }

        @Override
        public boolean next() throws JRException {
            if (iterator == null) {
                this.iterator = values.iterator();
            }
            boolean hasNext = iterator.hasNext();
            if (hasNext) {
                currentRecord = iterator.next();
            }
            return hasNext;
        }

        @Override
        public void moveFirst() throws JRException {
            iterator = null;
        }

        // ================================================================
        // Public or Protected Methods
        // ================================================================

        public void add(Object... values) {
            Map<String, Object> row = Maps.newHashMap();
            for (int i = 0; i < values.length; i++) {
                row.put(columns[i].getFieldName(), values[i]);
            }
            this.values.add(row);
        }
    }

    // ================================================================
    // Test Methods
    // ================================================================

}
